package com.borqsprocessmanager.sample;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;

import com.borqsprocessmanager.ConfigParams;
import com.borqsprocessmanager.info.ProcessStatsInfo;

public class ProcessPowerModule implements ASampleMoudle {
    private final String TAG = "ProcessPowerModule";

    private final String CLASS_NAME = "com.android.internal.os.ProcessStats";
    private final String STATS_CLASS_NAME = "com.android.internal.os.ProcessStats$Stats";
    private final String PROCESS_CLASS_NAME = "android.os.Process";

    private final String REL_UPTIME = "rel_uptime";
    private final String REL_UTIME = "rel_utime";
    private final String REL_STIME = "rel_stime";
    private final String PID = "pid";
    private final String NAME = "name";

    // private static ArrayList<ProcessStatsInfo> preInfoList;

    private Class processClass, statsClass, processOsClass;
    private Object invokeProcess, invokeStats;

    private PackageManager pm;

    private static boolean isSampleSystemPricess = false;

    public ProcessPowerModule(Context context) {

        try {
            pm = context.getPackageManager();
            processOsClass = Class.forName(PROCESS_CLASS_NAME);
            processClass = Class.forName(CLASS_NAME);
            statsClass = Class.forName(STATS_CLASS_NAME);
            Constructor[] cons = processClass.getConstructors();
            invokeProcess = cons[0].newInstance(false);
            init();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public ArrayList<ProcessStatsInfo> sample(int sample_id, int type,
            HashMap<String, ProcessStatsInfo> whiteMap) {
        ArrayList<ProcessStatsInfo> resList = null;
        switch (type) {
        case TYPE_GETDATA_AND_CONTINUE:
            if (isMoreThanTheBaseLine()) {
                resList = getProcessStats(sample_id, whiteMap);
                if (resList != null && resList.size() > 0) {
                    Log.v(TAG, "resList.size===" + resList.size());
                }
            }
        case TYPE_CONTINUE:
            init();
            break;
        }
        return resList;
    }

    @Override
    public ArrayList<ProcessStatsInfo> getSampleRes() {
        return null;
    }

    public void init() {
        Method initMethod;
        try {
            initMethod = processClass.getMethod("init", new Class[0]);
            initMethod.invoke(invokeProcess, new Object[0]);
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private boolean isMoreThanTheBaseLine() {
        Method getTotalCpuPercent;
        try {
            getTotalCpuPercent = processClass.getMethod("getTotalCpuPercent",
                    new Class[0]);
            float cpuPercent = (Float) getTotalCpuPercent.invoke(invokeProcess,
                    new Class[0]);
            Log.v(TAG, "cpuPercent===" + cpuPercent);
            if (cpuPercent > ConfigParams.CPU_TOTAL_BASELINE) {
                return true;
            }

        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    private ArrayList<ProcessStatsInfo> getProcessStats(int sampleId,
            HashMap<String, ProcessStatsInfo> whiteMap) {
        ArrayList<ProcessStatsInfo> list = new ArrayList<ProcessStatsInfo>();

        Method updateMethod;
        Method getStatsMethod;
        Method countStatsMethod;
        Method getUidForPid;

        int countStats = 0;
        int isSystemProcess = 0;
        try {
            getUidForPid = processOsClass.getMethod("getUidForPid", int.class);
            updateMethod = processClass.getMethod("update", new Class[0]);
            getStatsMethod = processClass.getMethod("getStats", int.class);
            countStatsMethod = processClass.getMethod("countStats",
                    new Class[0]);

            updateMethod.invoke(invokeProcess, new Object[0]);// update
            countStats = (Integer) countStatsMethod.invoke(invokeProcess,
                    new Object[0]);// getcountstats
            Log.v(TAG, "countStats===" + countStats);
            for (int j = 0; j < countStats; j++) {
                invokeStats = getStatsMethod.invoke(invokeProcess, j);// getStats

                Field pidField = statsClass.getField(PID);
                int pid = pidField.getInt(invokeStats);
                int uid = (Integer) getUidForPid.invoke(processClass, pid);
                if (uid < 10000) {
                    isSystemProcess = 1;
                } else {
                    isSystemProcess = 0;
                }
                if (isSystemProcess == 0 || isSampleSystemPricess) { // 设置一个开关“isSampleSystemPricess”，控制是否sample
                    // SystemProcee
                    {
                        Field uptimeField = statsClass.getField(REL_UPTIME);
                        long rel_uptime = uptimeField.getLong(invokeStats);

                        if (rel_uptime != 0) {
                            int totalTime = (int) (rel_uptime + 5) / 10;// note
                                                                        // about
                            // this
                            Field utimeField = statsClass.getField(REL_UTIME);
                            Field stimeField = statsClass.getField(REL_STIME);

                            int user = utimeField.getInt(invokeStats);// utime
                            int system = stimeField.getInt(invokeStats);// stime
                            int mysum = user + system;

                            if ((user & system) != 0) {
                                double ratio = getRatio(mysum, totalTime);
                                if (ratio > ConfigParams.CPU_PROCESS_BASELINE) {
                                    // Field nameField =
                                    // statsClass.getField(NAME);
                                    // String processName = (String) nameField
                                    // .get(invokeStats);
                                    String[] packageNames = pm
                                            .getPackagesForUid(uid);
                                    if (whiteMap.containsKey(packageNames[0])
                                            || packageNames[0]
                                                    .equals("com.borqsprocessmanager")) {
                                        com.borqsprocessmanager.util.Log.v(TAG,
                                                "whiteList_" + packageNames[0]);
                                        break;
                                    }

                                    ApplicationInfo appInfo = pm
                                            .getApplicationInfo(
                                                    packageNames[0],
                                                    PackageManager.GET_UNINSTALLED_PACKAGES);

                                    String processName = appInfo.processName;
                                    String appName = appInfo.loadLabel(pm)
                                            .toString();
                                    int icon = appInfo.icon;

                                    Log.v(TAG, "sampleId===" + sampleId);
                                    Log.v(TAG, "processName===" + processName);
                                    Log.v(TAG, "isSystemProcess==="
                                            + isSystemProcess);
                                    Log.v(TAG, "pid===" + pid);
                                    Log.v(TAG, "uid===" + uid);
                                    Log.v(TAG, "ratio===" + ratio);
                                    Log.v(TAG, "packageNames[0]==="
                                            + packageNames[0]);
                                    Log.v(TAG, "appName===" + appName);
                                    Log.v(TAG, "icon===" + icon);

                                    ProcessStatsInfo info = new ProcessStatsInfo();
                                    info.setSampleId(sampleId);
                                    info.setProcessName(processName);
                                    info.setIsSystem(isSystemProcess);
                                    // info.setPid(pid);
                                    info.setUid(uid);
                                    info.setRatio(ratio);
                                    info.setPackageName(packageNames[0]);
                                    info.setAppName(appName);
                                    info.setIcon(icon);
                                    list.add(info);
                                }
                            }
                        }
                    }
                }
            }
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // mStat.update();// sample
        catch (NameNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return list;
    }

    private double getRatio(long numerator, long denominator) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);

        long thousands = (numerator * 1000) / denominator;
        long hundreds = thousands / 10;
        pw.print(hundreds);
        if (hundreds < 10) {
            long remainder = thousands - (hundreds * 10);
            if (remainder != 0) {
                pw.print('.');
                pw.print(remainder);
            }
        }
        String rs = sw.toString();
        return Double.parseDouble(rs);
    }
}
